package com.boat.starter.satoken.utils;

import com.boat.framework.common.security.model.LoggedUser;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaStorage;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 登录鉴权助手
 * <p>
 * user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app deivce 为 设备类型 同一个用户类型 可以有 多种设备类型
 * 例如 web,ios 可以组成 用户类型与设备类型多对多的 权限灵活控制
 * <p>
 * 多用户体系 针对 多种用户类型 但权限控制不一致 可以组成 多用户类型表与多设备类型 分别控制权限
 *
 * @author Lion Li
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SaTokenUtils {

	public static final String LOGGED_USER_KEY = "loggedUser";
	public static final String USER_ID_KEY = "userId";
	public static final String USER_NAME_KEY = "userName";
	public static final String USER_TYPE_KEY = "userType";
	/**
	 * 管理员ID
	 */
	private static String SUPER_ADMIN_ID = "1";
	
	/**
	 * 登录系统 基于 设备类型 针对相同用户体系不同设备
	 *
	 * @param loginUser 登录用户信息
	 * @param model     配置参数，可以为null
	 */
	public static LoginResult login(LoggedUser loginUser, SaLoginModel model) {
		// 1.写数据到线程上下文中
		SaStorage storage = SaHolder.getStorage();
		storage.set(LOGGED_USER_KEY, loginUser);
		storage.set(USER_ID_KEY, loginUser.getUserId());
		storage.set(USER_NAME_KEY, loginUser.getUserName());
		storage.set(USER_TYPE_KEY, loginUser.getUserType());

		// 2.写数据到token字符串中
		model = ObjectUtil.defaultIfNull(model, new SaLoginModel());
		model.setExtra(USER_ID_KEY, loginUser.getUserId());
		model.setExtra(USER_NAME_KEY, loginUser.getUserName());
		model.setExtra(USER_TYPE_KEY, loginUser.getUserType());
//		if (model.getTimeout() == null) {
//			long timeout = SpringUtils.getBean(AuthProperties.class).getSatoken().getTimeout();
//			model.setTimeout(timeout);
//		}
		StpUtil.login(loginUser.getUserId(), model);

		// 3.写数据到redis缓存
		StpUtil.getSession().set(LOGGED_USER_KEY, loginUser);

		//4.返回登录结果
		return new LoginResult(StpUtil.getTokenValue(), StpUtil.getTokenTimeout());
	}

	/**
	 * 当前用户退出登录
	 */
	public static void logout() {
		StpUtil.logout();
	}

	/**
	 * 获取用户(多级缓存)
	 */
	public static LoggedUser getLoginUser() {
		LoggedUser loginUser = (LoggedUser) SaHolder.getStorage().get(LOGGED_USER_KEY);
		if (loginUser != null) {
			return loginUser;
		}
		SaSession session = StpUtil.getSession();
		if (ObjectUtil.isNull(session)) {
			return null;
		}
		loginUser = (LoggedUser) session.get(LOGGED_USER_KEY);
		SaHolder.getStorage().set(LOGGED_USER_KEY, loginUser);
		return loginUser;
	}

	/**
	 * 获取用户基于token
	 */
	public static LoggedUser getLoginUser(String token) {
		Object loginId = StpUtil.getLoginIdByToken(token);
		SaSession session = StpUtil.getSessionByLoginId(loginId);
		if (ObjectUtil.isNull(session)) {
			return null;
		}
		return (LoggedUser) session.get(LOGGED_USER_KEY);
	}

	/**
	 * 获取用户id
	 */
	public static String getUserId() {
		return Convert.toStr(getExtra(USER_ID_KEY));
	}

	/**
	 * 获取用户名
	 */
	public static String getUsername() {
		return Convert.toStr(getExtra(USER_NAME_KEY));
	}

	/**
	 * 获取用户类型
	 */
	public static String getUserType() {
		return Convert.toStr(getExtra(USER_TYPE_KEY));
	}

	private static Object getExtra(String key) {
		Object obj;
		try {
			obj = SaHolder.getStorage().get(key);
			if (ObjectUtil.isNull(obj)) {
				obj = StpUtil.getExtra(key);
				SaHolder.getStorage().set(key, obj);
			}
		} catch (Exception e) {
			return null;
		}
		return obj;
	}

	/**
	 * 是否为超级管理员
	 *
	 * @param userId 用户ID
	 * @return 结果
	 */
	public static boolean isSuperAdmin(String userId) {
		return SUPER_ADMIN_ID.equals(userId);
	}

	public static boolean isSuperAdmin() {
		return isSuperAdmin(getUserId());
	}
	
	public static void setSuperAdminId(String userId) {
		SUPER_ADMIN_ID = userId;
	}

	/**
	 * 登录结果
	 * 
	 * @author 周江
	 *
	 */
	@Data
	@NoArgsConstructor
	@AllArgsConstructor
	public static class LoginResult {
		/**
		 * 生成的token
		 */
		private String token;
		/**
		 * 过期时间，单位秒
		 */
		private long timeout;
	}
}
